package model;

import com.javadocmd.simplelatlng.LatLng;
import com.javadocmd.simplelatlng.LatLngTool;
import com.javadocmd.simplelatlng.util.LengthUnit;

public class InformacionTramo {

	private LatLng puntoInicial;
	private LatLng puntoFinal;
	private double longitudTramo;
	private double distanciaAcumulada;
	private static final double ERROR_CALCULO = 0.0000000199999;
	private static final double ERROR_COORDENADAS = 0.000009999;

	/**
	 * Constructor tramo.
	 * 
	 * @param pInicial
	 * @param pFinal
	 */
	public InformacionTramo(LatLng pInicial, LatLng pFinal) {
		this.puntoInicial = pInicial;
		this.puntoFinal = pFinal;
	}

	/**
	 * Evalua si la distancia pasada por parametro corresponde al tramo.
	 * 
	 * @param distancia
	 * @return boolean true si la distancia pertenece al tramo, false si no
	 *         pertenece
	 */
	public boolean distanciaDentroDeTramo(double distancia) {
		double inicio = distanciaAcumulada - longitudTramo;
		double fin = distanciaAcumulada;

		return (distancia <= fin && distancia >= inicio);
	}

	/**
	 * Obtiene la distancia de un punto en la ruta.
	 * 
	 * @param latitud
	 * @param longitud
	 * @return
	 */
	public double obtenerDistanciaEnRuta(double latitud, double longitud) {
		LatLng puntoFinal = new LatLng(latitud, longitud);
		return this.distanciaAcumulada - longitudTramo + LatLngTool.distance(puntoInicial, puntoFinal, LengthUnit.METER);
	}

	/**
	 * Evalua si un punto esta contenido dentro del tramo.
	 * 
	 * @param latitud
	 * @param longitud
	 * @return true si lo es, false si no lo esta
	 */
	public boolean puntoDentroDelTramo(double latitud, double longitud) {

		double error = (longitud - puntoFinal.getLongitude()) * (puntoFinal.getLatitude() - puntoInicial.getLatitude())
				- (latitud - puntoFinal.getLatitude()) * (puntoFinal.getLongitude() - puntoInicial.getLongitude());
		boolean errorAceptable = errorEnRangoAceptable(error);

		if (errorAceptable) {
			boolean contenidoEncoord = puntoContenidoEntreCoordenadas(latitud, longitud);
			if (contenidoEncoord) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Determina si el punto a evaluar se encuentra entre las coordenadas que
	 * forman el tramo.
	 * 
	 * @param latitud
	 * @param longitud
	 * @return boolean true si esta contenido, false si no lo esta
	 */
	private boolean puntoContenidoEntreCoordenadas(double latitud, double longitud) {

		double latMayor = Math.max(puntoInicial.getLatitude(), puntoFinal.getLatitude()) + ERROR_COORDENADAS;
		double latMenor = Math.min(puntoInicial.getLatitude(), puntoFinal.getLatitude()) - ERROR_COORDENADAS;
		double lonMayor = Math.max(puntoInicial.getLongitude(), puntoFinal.getLongitude()) + ERROR_COORDENADAS;
		double lonMenor = Math.min(puntoInicial.getLongitude(), puntoFinal.getLongitude()) - ERROR_COORDENADAS;

		if (((latitud >= latMenor) && (latitud <= latMayor)) && ((longitud >= lonMenor) && (longitud <= lonMayor))) {
			return true;
		}
		return false;
	}

	/**
	 * Determina si el error para determinar si los puntos estan alineados es
	 * aceptable.
	 * 
	 * @param error
	 * @return boolean true si es aceptable, false si no lo es
	 */
	private boolean errorEnRangoAceptable(double error) {
		double limiteInferior = -ERROR_CALCULO;
		double limiteSuperior = ERROR_CALCULO;
		if (error < limiteSuperior && error > limiteInferior) {
			return true;
		}
		return false;
	}

	// getters y setters
	public LatLng getPuntoFinal() {
		return puntoFinal;
	}

	public void setPuntoFinal(LatLng puntoFinal) {
		this.puntoFinal = puntoFinal;
	}

	public double getLongitudTramo() {
		return longitudTramo;
	}

	public void setLongitudTramo(double longitudTramo) {
		this.longitudTramo = longitudTramo;
	}

	public double getDistanciaAcumulada() {
		return distanciaAcumulada;
	}

	public void setDistanciaAcumulada(double distanciaAcumulada) {
		this.distanciaAcumulada = distanciaAcumulada;
	}

	public LatLng getPuntoInicial() {
		return puntoInicial;
	}

	public void setPuntoInicial(LatLng puntoInicial) {
		this.puntoInicial = puntoInicial;
	}

}
